/* 

==========================================================

DX490a - Summer 2010

Instructor: Stelios Manousakis

==========================================================

Class 10.1:

Patterns in the server: Analog-style control signals and Demand UGens

Contents:

• Analog-style control signals

- Counters

> PulseCount

> Stepper

> PulseDivider

- Selectors

> Select

- Sample-and-Hold

• Demand UGens

- a list of Demand UGens (June 2010)

==========================================================

*/



// ================= ANALOG-STYLE CONTROL SIGNALS =================


// SynthDefs don't have to be completely static: you can schedule things using analog-studio-style controls inside a synthdef, sequencing signals with signals - there are a few different UGens to do that. Most importantly, however, and breaking out from the analog paradigm, you can use Demand UGens to create signal patterns. 





// ====== COUNTERS ======


// ------ PulseCount --

// PulseCount is the simplest, most basic signal counter. You provide it with a trigger and it starts counting integers from 0, until you reset it:


SynthDef("help-PulseCount",{ arg out=0;

Out.ar(out,

SinOsc.ar(

(PulseCount.ar(Impulse.ar(8), Impulse.ar(1.2)).poll * 200) + 100, 

0, 0.25

)

)

}).play;




// ------ Stepper --

// Stepper is a counter that you can trigger and reset with a signal like PulseCount, except you can also define the minimum and maximum values, as well as the step size. 


SynthDef("help-Stepper",{ arg out=0;

Out.ar(out,

SinOsc.ar(

Stepper.kr(

Impulse.kr(7), // trigger

0, // reset

5, // min value

17, // max value

1,  // step value

nil) // reset value

* 100, 

0, 0.25

)

)

}).play;


// much nicer when resetting and using a big enough step to make the pattern foldover:

SynthDef("help-Stepper",{ arg out=0;

Out.ar(out,

SinOsc.ar(

Stepper.kr(

Impulse.kr(7), // trigger

Impulse.kr(0.25), // reset

5, // min value

17, // max value

5,  // step value

3) // reset value

* 100, 

0, 0.25

)

)

}).play;





// ------ PulseDivider --

// PulseDivider does what its name says: outputs an impulse after receiving a specified number of triggers in its input.

// Here 's a cool little example by the ixi people


(

SynthDef(\drummer, { arg out=0, tempo=4, amp = 1;

var snare, base, hihat;

tempo = Impulse.ar(tempo); // for a drunk drummer replace Impulse with Dust !!!

snare = WhiteNoise.ar(Decay2.ar(PulseDivider.ar(tempo, 4, 2), 0.005, 0.5));

base = SinOsc.ar(Line.ar(120,60, 1), 0, Decay2.ar(PulseDivider.ar(tempo, 4, 0), 0.005, 0.5));

hihat = HPF.ar(WhiteNoise.ar(1), 10000) * Decay2.ar(tempo, 0.005, 0.5);

Out.ar(out, ((snare + base + hihat) * 0.4!2) * amp)

}).load(s);

)


a = Synth(\drummer);

a.set(\tempo, 6);

a.set(\tempo, 18, \amp, 0.75);

a.set(\tempo, 180, \amp, 0.2); // check the CPU! no increase.





// ====== SELECTORS ======


// The Select UGens are very handy for switching or mixing between members of an array; these can be sequences of values, or even different UGens. Be warned though, as this is server-side, all the UGens are constantly running, regardless of wether you can hear them or not.


// ------ Select --


// Here is yet one more not very musical example (but you get the idea):

SynthDef(\SelectSeq,{ arg outbus = 0;

var array;

array = [LFSaw.kr(1),  LFPulse.kr(3), LFDNoise1.kr(2), LFTri.kr(0.5), LFPulse.kr(5), DC.ar(0.5)];

Out.ar(outbus,

SinOsc.ar(

(SelectX.kr(LFDNoise0.kr(5) * array.size, array) * 440).abs + 220, 

0, 0.25

)

)

}).play;


// Or, going through an array instead of using UGens

SynthDef(\SelectSeq,{ arg outbus = 0;

var array;

array = Array.series(12, 60, 1).midicps; // create an octave starting at middle C

Out.ar(outbus,

SinOsc.ar(

Select.kr(LFDNoise0.kr(5) * array.size, array), 

0, 0.25)

)

}).play;


// There is also SelectX, which lets you mix and crossfade between adjacent member of the array. Also, you may want to check out [SelectL], [RotateN], and [RotateL], all part of the wslib quark.





// ====== SAMPLE-AND-HOLD ======

// This is the most 'traditional', analog studio technique: having a signal run and using another signal to sample it at regular or irregular intervals. The SC object that does that is called Latch. Latch is audio rate, and the signal it samples also needs to be audio rate.


SynthDef(\sampleAndHold,{ arg outbus = 0;

Out.ar(outbus,

SinOsc.ar(

Latch.ar(WhiteNoise.ar(440, 220) , Impulse.ar(7)),

0, 0.25)

)

}).play;


// Let's make the trigger signal a bit more interesting (this is the very basics of a clock idea I developed in the analog studio at the Institute of Sonology):

SynthDef(\sampleAndHold,{ arg outbus = 0;

var trig;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

Out.ar(outbus,

SinOsc.ar(

Latch.ar(WhiteNoise.ar(440, 220), trig),

0, 0.25)

)

}).play;





// ================= DEMAND UGENS =================


// With Demand UGens you can implement pattern/stream-style behavior in the server, inside a SynthDef.  You provide a trigger and get a signal output calculated from the Demand UGen. There is quite a few different behaviors implemented, with several Demand UGens existing in SC - their names begin with 'D' so they are easy to spot. What's nice about them, is that they are very efficient, as they use a 'lazy' implementation, and you can get specific values out of them without worrying about the timing of your triggers compared to your control signal (which would not be the case with a more standard sample-and-hold technique). Also, there is quite a lot of options.


// The fundamental UGen here is Demand. This is the UGen that you need to use to get data from the rest of the Demand UGens. You provide it a trigger, and it demands a value form the UGen or UGens in its list. You can also reset it, in which case Demand will reset all the UGens it contains. Demand is essentially a Sample-and-Hold mechanism, but it is different than Latch in that it can handle all the Demand UGen bureaucracy.


// Here is the same example as above, but rewritten with Demand instead of Latch:

SynthDef(\sampleAndHold,{ arg outbus = 0;

var trig;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, WhiteNoise.ar(440, 220)),

0, 0.25)

)

}).play;



// Now, using some of the Demand UGens:

// • Dseries: generate arithmetic series

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

pat = Dseries(0, 1, inf) % 13; // generate an infinite series of values, starting from 0 and with a step-size of 1, and have them fold between 0 and 13

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, (pat * 440) + 220),

0, 0.25)

)

}).play;



// • Dgeom: generate geometric series

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

pat = Dgeom(1, 1.2, inf) % 13; // generate an infinite series of values, starting from 0 and with a step-size of 1, and have them fold between 0 and 13

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, (pat * 440) + 220),

0, 0.25)

)

}).play;


// now, let's use Demand's reset input to bring it back:

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

pat = Dgeom(1, 1.2, inf) % 13; // generate an infinite geometric series of values, starting from 0 and growing by 1.2, and have them fold between 0 and 13

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, Dust.ar(0.25), (pat * 440) + 220),

0, 0.25)

)

}).play;



// • Dseq: play back a sequence

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness

pat = Dseq([1, 4, 5, 2, 7, 5, 8], inf); // play back the sequence infinite times

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, (pat * 440) + 220), 

0, 0.25)

)

}).play;



// • Dshuf: shuffle a sequence

// Now, for a more complex example, using two demand ugens, one embedded in the other:

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7); // simple clock to hear what's going on

pat = Dseq([Dshuf([1, 4, 5, 2, 7, 5, 8], 4)], inf); // play back the shuffled sequence four times, then play another shuffle; repeat infinite times

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, (pat * 440) + 220), 

0, 0.25)

)

}).play;



// • now, for an even more complex example, using Dbrown, Dstutter  and Dwhite as well

SynthDef(\demand,{ arg outbus = 0;

var trig, pat;

trig = Impulse.ar(7); // simple clock to hear what's going on

pat = Dseq(

[Dshuf(

[1, 4, 5, 2, 7, 5, 8], 3), 

Dbrown(1, 12, 1, 7), 

Dstutter(3, Dwhite(1, 12, 7))],

inf); // play back the shuffled sequence three times, then play another seven values from dbrown; repeat infinite times

Out.ar(outbus,

SinOsc.ar(

Demand.ar(trig, 0, (pat * 440) + 220), 

0, 0.25)

)

}).play;



// • Duty and TDuty have their own triggering system, so you can use another Demand UGen to specify a stream of durations 

SynthDef(\demand,{arg outbus = 0;

var freq;

freq = Duty.kr(

Drand([0.01, 0.2, 0.4], inf), // demand ugen as durations

0, 

Dseq([204, 400, 201, 502, 300, 200], inf)

); 

Out.ar(outbus, SinOsc.ar(freq * [1, 1.01]) * 0.1);

}).play;


// Demand UGens can run at audio rate as well:

SynthDef(\demand,{arg outbus = 0;

var freq;

freq = Duty.kr(

Drand([0.01, 0.2, 0.4] * MouseX.kr(0.001, 3, 1), inf), // demand ugen as durations

0, 

Dseq([204, 400, 201, 502, 300, 200], inf)

); 

Out.ar(outbus, SinOsc.ar(freq * [1, 1.01]) * 0.1);

}).play;





// ------ Demand UGen list (June 2010) --

/*

Dbrown, Dibrown demand rate brownian movement generators

Dbufrd buffer demand ugen

DbufTag demand rate tag system on a buffer

Dbufwr buffer demand ugen

Demand demand results from demand rate ugens

DemandEnvGen demand rate envelope generator

Dfsm demand rate finite state machine

Dgeom demand rate geometric series ugen

Donce

Dpoll print the current output value of a demand rate UGen

Drand, Dxrand demand rate random sequence generators

Dseq demand rate sequence generator

Dser demand rate sequence generator

Dseries demand rate arithmetic series ugen

Dshuf demand rate random sequence generator

Dstutter demand rate input replicator

Dswitch demand rate generator for embedding different inputs

Dswitch1 demand rate generator for switching between inputs

Dtag demand rate tag system

DUGen

Duty demand results from demand rate ugens

Dwhite, Diwhite demand rate white noise random generators

ListDUGen

TDuty demand results as trigger from demand rate ugens

// Also:

Dbrown2 demand rate brownian movement with Gendyn distributions

*/